//This class is the structure of Workflows
package it.uniroma2.structure.tree;

import it.uniroma2.structure.Process;
import it.uniroma2.structure.*;
import java.util.Vector;

public class WorkflowTree {
	
	private Vector<WorkflowTreeNode> tree;
	private Vector<WorkflowTreeNode> subtree;
	private Vector<WorkflowTreeNode> subsubtree;
	private Vector<Service> initialService;
	private Vector<Service> allServ;
	private Vector<WorkflowTreeNode> classi1;
	private Vector<WorkflowTreeNode> classi2;
	
	public Vector<Vector<Integer>> listWorkflow;
	
	private Vector<WorkflowTreeNode> prev = new Vector<WorkflowTreeNode>();
	
	
	
	public WorkflowTree(Vector<Service> initialService, Vector<Service> allServ){
		
		
		this.initialService = initialService;
		this.allServ = allServ;
		this.tree = new Vector<WorkflowTreeNode>();
		this.subtree = new Vector<WorkflowTreeNode>(); 
		this.classi1 = new Vector<WorkflowTreeNode>();
		this.classi2 = new Vector<WorkflowTreeNode>();
		this.listWorkflow = new Vector<Vector<Integer>>();
		this.fillTree();
		this.buildTree();
		this.buildSubTree(classi1);
		this.putEnd();
		this.navigateTree();
		
		
		for (int i = 0; i < subtree.size(); i++){
			System.out.println("Ecco: " + subtree.elementAt(i).getService().getNome());
		}
		
	}
	
	private void fillTree(){

		boolean r1 = false, r2 = false, r3 = false;
		for (int i = 0; i < initialService.size(); i++){
			Service tmp = initialService.elementAt(i);
			if (tmp.getRank() == 1) r1 = true;
			if (tmp.getRank() == 2) r2 = true;
			if (tmp.getRank() == 3) r3 = true;
		}
		if (r1 && r2 && r3){
			for (int i = 0; i < initialService.size(); i++){
				Service tmp = initialService.elementAt(i);
				if(tmp.getRank() == 1){
					this.tree.add(new WorkflowTreeNode(tmp));
					this.classi1.add(new WorkflowTreeNode(tmp));
				}
				if(tmp.getRank() == 2){
					this.subtree.add(new WorkflowTreeNode(tmp));
					this.classi2.add(new WorkflowTreeNode(tmp));
				}
				if(tmp.getRank() == 3){
					this.subsubtree.add(new WorkflowTreeNode(tmp));
				}
				
			}
		} else if (r1 && r2 && !r3){
			for (int i = 0; i < initialService.size(); i++){
				Service tmp = initialService.elementAt(i);
				if(tmp.getRank() == 1){
					this.tree.add(new WorkflowTreeNode(tmp));
					this.classi1.add(new WorkflowTreeNode(tmp));
				}
				if(tmp.getRank() == 2){
					this.subtree.add(new WorkflowTreeNode(tmp));
				}
			}
		} else if(r1 && !r2 && !r3){
			for (int i = 0; i < initialService.size(); i++){
				Service tmp = initialService.elementAt(i);
				if(tmp.getRank() == 1){
					this.tree.add(new WorkflowTreeNode(tmp));
				}
				
			}
		} else if (r1 && !r2 && r3){
			for (int i = 0; i < initialService.size(); i++){
				Service tmp = initialService.elementAt(i);
				if(tmp.getRank() == 1){
					this.tree.add(new WorkflowTreeNode(tmp));
					this.classi1.add(new WorkflowTreeNode(tmp));
				}
				if(tmp.getRank() == 3){
					this.subtree.add(new WorkflowTreeNode(tmp));
				}
				
			}
		} else if (!r1 && r2 && !r3){
			for (int i = 0; i < initialService.size(); i++){
				Service tmp = initialService.elementAt(i);
				if(tmp.getRank() == 2){
					this.tree.add(new WorkflowTreeNode(tmp));
				}
			}
		} else if(!r1 && !r2 && r3){
			for (int i = 0; i < initialService.size(); i++){
				Service tmp = initialService.elementAt(i);
				if(tmp.getRank() == 3){
					this.tree.add(new WorkflowTreeNode(tmp));
				}
			}
		} else if (!r1 && r2 && r3){
			for (int i = 0; i < initialService.size(); i++){
				Service tmp = initialService.elementAt(i);
				if(tmp.getRank() == 2){
					this.tree.add(new WorkflowTreeNode(tmp));
					this.classi1.add(new WorkflowTreeNode(tmp));
				}
				if(tmp.getRank() == 3){
					this.subtree.add(new WorkflowTreeNode(tmp));
				}
			}
		}
	}
	
	private void buildTree(){
		for(int i = 0; i < this.tree.size(); i++){
			Service tmpService = new Service();
			WorkflowTreeNode tmp = this.tree.elementAt(i);
			Vector<String> pre = new Vector<String>();
			pre = tmp.getService().getPreCondizioneNoLoop();
			if(pre != null){
			for (int k = 0; k < pre.size(); k++){
				boolean present = false;
				for (int j = 0; j < allServ.size(); j++){
					if(allServ.elementAt(j).getNome().equals(pre.elementAt(k))){
						tmpService = allServ.elementAt(j);
					}
				}
				for (int j = 0; j < tree.size(); j++){
					if(tree.elementAt(j).getService().getNome().equals(tmpService.getNome())){
						tree.elementAt(j).setFatherOf(tree.elementAt(i).getId());
						present = true;
						this.tree.elementAt(i).child();
					}
				}
				if(!present){
					WorkflowTreeNode wtn = new WorkflowTreeNode(tmpService);
					wtn.setFatherOf(tree.elementAt(i).getId());
					tree.addElement(wtn);	
					this.tree.elementAt(i).child();
				}
				
			}
		}
	}
	}
	
	private void buildSubTree(Vector<WorkflowTreeNode> classifierTn){
		for(int i = 0; i < this.subtree.size(); i++){
			Service tmpService = new Service();
			WorkflowTreeNode tmp = this.subtree.elementAt(i);
			Vector<String> pre = new Vector<String>();
			pre = tmp.getService().getPrecondizioneNoNLP();
			if(pre != null){
				for (int k = 0; k < pre.size(); k++){
					boolean present = false;
					for (int j = 0; j < allServ.size(); j++){
						if(allServ.elementAt(j).getNome().equals(pre.elementAt(k))){
							tmpService = allServ.elementAt(j);
						}
					}
					for (int j = 0; j < subtree.size(); j++){
						if(subtree.elementAt(j).getService().getNome().equals(tmpService.getNome())){
							subtree.elementAt(j).setFatherOf(subtree.elementAt(i).getId());
							present = true;
							this.subtree.elementAt(i).child();
						}
					}
					if(!present){
						WorkflowTreeNode wtn = new WorkflowTreeNode(tmpService);
						wtn.setFatherOf(subtree.elementAt(i).getId());
						subtree.addElement(wtn);
						this.subtree.elementAt(i).child();
					}
					
				}
			}
		}
		for(int i = 0; i < this.tree.size(); i++){
			WorkflowTreeNode tmp = this.tree.elementAt(i);
			if(tmp.getFatherVect().isEmpty()){
				for(int j = 0; j < subtree.size(); j++){
					if(!subtree.elementAt(j).isChild()){
						tree.elementAt(i).setFatherOf(subtree.elementAt(j).getId());
					}
				}
			}
		}
		//Add all services in main tree
		for(int i = 0; i < subtree.size(); i++){
			tree.add(subtree.elementAt(i));
		}
	}
	
	public void putEnd(){
		Service tmpService = new Service();
		for (int j = 0; j < allServ.size(); j++){
			if(allServ.elementAt(j).getNome().equals("End")){
				tmpService = allServ.elementAt(j);
			}
		}
		WorkflowTreeNode wtn = new WorkflowTreeNode(tmpService);
		for(int i = 0; i < this.tree.size(); i++){
			WorkflowTreeNode tmp = this.tree.elementAt(i);
			if(tmp.getFatherVect().isEmpty()){
				this.tree.elementAt(i).setFatherOf(wtn.getId());
			}
		}
		wtn.setEnd();
		this.tree.add(wtn);
	}
	
	public Vector<WorkflowTreeNode> getTree(){
		return tree;
	}
	
	//This method navigates tree and create all the possible workflow
	private void navigateTree(){
		
		boolean stop = false;
		
		//This vector rapresents the way to navigate the tree
		Vector<Integer> walk = new Vector<Integer>();
		for(int i = 0; i < 15; i++){
			walk.add(0);
		}
		//Initial position
		int pos = 0;
		
		WorkflowTreeNode start = null;
		WorkflowTreeNode currentWtn = null;
		for (int j = 0; j < tree.size(); j++){
			if(tree.elementAt(j).getService().getNome().equals("Start")){
				start = tree.elementAt(j);
			}
		}
		currentWtn = start;
		
		
		while(!stop){
			
			while(!currentWtn.isEnd() && !currentWtn.isOut() && !stop){
				int walkFath = currentWtn.getFatherOf(walk.elementAt(pos));
				if(walkFath == -1){
					pos--;
					if(pos < 0){
						stop = true;
						break;
					}
					walk.setElementAt(walk.elementAt(pos) + 1, pos);
					for(int k = pos + 1; k < walk.size(); k++){
						walk.setElementAt(0, k);
					}
					currentWtn = getPrev();
					
				} else {
				prev.add(currentWtn);
				currentWtn = findWtn(walkFath);
				pos++;
				}
			}
			//Previous pro
			if(currentWtn.isEnd()){
				Vector<Integer> wf = new Vector<Integer>();
				for(int i = 0; i < pos - 1; i++){
					wf.add(walk.elementAt(i));
				}
					this.saveWorkflow(wf);
					pos--;
					walk.setElementAt(walk.elementAt(pos) + 1, pos);
					for(int k = pos + 1; k < walk.size(); k++){
						walk.setElementAt(0, k);
					}	
					currentWtn = getPrev();
				
			} else if(currentWtn.isOut()){
				pos--;
				walk.setElementAt(walk.elementAt(pos) + 1, pos);
				for(int k = pos + 1; k < walk.size(); k++){
					walk.setElementAt(0, k);
				}
				currentWtn = getPrev();
			}
			
		}
		
		
		
	}
	
	public WorkflowTreeNode findWtn(int id){
		for (int i = 0; i < tree.size(); i++){
			if(tree.elementAt(i).getId() == id){
				return tree.elementAt(i);
			}
		}
		return null;
	}
	
	private void saveWorkflow(Vector<Integer> wf){
		this.listWorkflow.add(wf);
	}
	
	private WorkflowTreeNode getPrev(){
		WorkflowTreeNode tmp = this.prev.lastElement();
		this.prev.removeElementAt(this.prev.size() - 1);
		return tmp;
	}
	
	//This method eliminate a service from workflows due to a rule
	public void shutService(String serviceName){
		for(int i = 0; i < this.tree.size(); i++){
			if(this.tree.elementAt(i).getService().getNome().equals(serviceName)){
				this.tree.elementAt(i).eliminateService();
			}
		}
		
	}
	
	//Mwthod that creates a vector of Workflowtreenode which is the worflow without Start and End
	private Vector<WorkflowTreeNode> generateSingleWorkflow(Vector<Integer> workflow){
		WorkflowTreeNode currentWtn = null;
		WorkflowTreeNode start = null;
		Vector<WorkflowTreeNode> wtnVec = new Vector<WorkflowTreeNode>();
		for (int j = 0; j < tree.size(); j++){
			if(tree.elementAt(j).getService().getNome().equals("Start")){
				start = tree.elementAt(j);
			}
		}
		currentWtn = start;
		for(int i = 0; i < workflow.size() ; i++){
			currentWtn = findWtn(currentWtn.getFatherOf(workflow.elementAt(i)));
			wtnVec.add(currentWtn);
		}
		return wtnVec;
	}
	
	//This method create a process hence a BPEL of selected workflow
	public Process createProcess(Vector<Integer> workflow){
		Process process = new Process();
		Node main = process.getMainNode();
		Node parent = main;
		Node previous = null;
		
		
		Vector<WorkflowTreeNode> wtn = this.generateSingleWorkflow(workflow);
		
		for(int i = 0; i < wtn.size(); i++){
			if(wtn.elementAt(i).isIf()){
				
			} else {
				Service tmpService = wtn.elementAt(i).getService();
				process.addNode(parent, previous, tmpService);
				Parameter toServ = null;
				if(i == 0){
					LinkServiceNode lsnStart = process.addInputLink(tmpService);
					for(int j = 0; j < tmpService.getParameterIn().size(); j++){
						if(tmpService.getParameterIn().elementAt(j).getTypeParameter().equals("fromservice")){
							toServ = tmpService.getParameterIn().elementAt(j);
						}
					}
					lsnStart.addCopy(((Main_Sequence_Control)process.getMainNode()).getParameterIn(), toServ);
					
				} else if(i == wtn.size() - 1) {
					
					LinkServiceNode lsn = process.addLinkNode(previous, tmpService);
					
					for(int j = 0; j < tmpService.getParameterIn().size(); j++){
						if(tmpService.getParameterIn().elementAt(j).getTypeParameter().equals("fromservice")){
							toServ = tmpService.getParameterIn().elementAt(j);
						}
					}
					lsn.addCopy(((Service)previous).getParameterOut().firstElement(), toServ);
					
					
					LinkServiceNode lsnEnd = process.addOutputLink(tmpService);
					lsnEnd.addCopy(tmpService.getParameterOut().firstElement(), ((Main_Sequence_Control)process.getMainNode()).getParameterOut());
				} else {
					LinkServiceNode lsn = process.addLinkNode(previous, tmpService);
					
					for(int j = 0; j < tmpService.getParameterIn().size(); j++){
						if(tmpService.getParameterIn().elementAt(j).getTypeParameter().equals("fromservice")){
							toServ = tmpService.getParameterIn().elementAt(j);
						}
					}
					lsn.addCopy(((Service)previous).getParameterOut().firstElement(), toServ);
					
					
				}
				previous = tmpService;
			}
			
		}
		
//		System.out.println(process.save());
//		process.saveWorkflow();
		
		
		return process;
		
		
		
	}
}



